#include "stdafx.h"

#include "CXView.h"
#include "CXComp.h"
#include "MainFrame.h"
#include <icache/cache.h>
#include <imglib/winimage.h>
#include "Claxa.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CXView

CXView::CXView(CXComp & c) :
	m_Comp(c),
	m_MaskEditor(* this),
	m_pFollowSegment(0),
	m_CompEvents(c, * this), 
	m_SegmentSetEvents(c.GetSegments(), * this), 
	m_MaskEditorEvents(m_MaskEditor, * this),
	m_BackgroundEvents(m_Comp.GetBackground(), * this)
{
	m_Style.ReadSettings();
	m_RAMPreview = theApp.GetProfileInt("Display", "RAMPreview", 0) != 0;
}

CXView::~CXView()
{
	m_Style.WriteSettings();
	theApp.WriteProfileInt("Display", "RAMPreview", m_RAMPreview ? 1 : 0);
}

BEGIN_MESSAGE_MAP(CXView, SequenceView)
	ON_COMMAND(ID_TIME_RAM_PREVIEW, OnRamPreview)
	ON_UPDATE_COMMAND_UI(ID_TIME_RAM_PREVIEW, OnUpdateRamPreview)
	ON_COMMAND(ID_DISPLAY_STYLE_MASK_ONLY, SetMaskOnlyDisplayType)
	ON_COMMAND(ID_DISPLAY_STYLE_FOREGROUND_ONLY, SetForegroundOnlyDisplayType)
	ON_COMMAND(ID_DISPLAY_STYLE_BACKGROUND_ONLY, SetBackgroundOnlyDisplayType)
	ON_COMMAND(ID_DISPLAY_STYLE_MATTED_FOREGROUND, SetMattedForegroundDisplayType)
	ON_COMMAND(ID_DISPLAY_STYLE_COMPOSITION, SetCompositionDisplayType)
END_MESSAGE_MAP()

// CXView message handlers

My::CachedImageSPtr CXView::GetPainting(PaintingKeySPtr const & p, icache::nearby nb)
{
	return ::GetPainting(p, nb, m_RAMPreview);
}

My::Sequence * CXView::GetSequence() const
{
	return &m_Comp;
}

CDC * CXView::GetOffscreenDC()
{
	return static_cast<MainFrame*>(GetParentFrame())->GetOffDC();
}

COLORREF CXView::GetBackgroundColor() const
{
	if (m_Comp.IsEmpty())
		return GetSysColor(COLOR_APPWORKSPACE);
	else {
		const ByteColor & c = m_Style.MatteColor;
		return RGB(c.r, c.g, c.b);
	}
}

void CXView::SetBackgroundColor(COLORREF c)
{
	Base::SetBackgroundColor(c);
	DisplayStyle s = m_Style;
	s.MatteColor = ByteColor(GetBValue(c), GetGValue(c), GetRValue(c), s.MatteColor.a);
	SetDisplayStyle(s);
}

void CXView::PaintImageRect(CDC * pDC, CRect const & rClient, CRect const & rImage)
{
	BufferSPtr BufferCopy = GetBufferForDisplaying();
	if (!BufferCopy)
		return;

	if (EditingMask()) {
		m_EditMaskOnlyMask = m_Style.Type == MaskOnly;
		m_EditMaskInverse = m_Style.InverseMask;
		m_EditMaskMatte = m_Style.MatteColor;

		size_point RectTopLeft(rImage.left, rImage.top);
		ByteImage Blend(rImage.Width(), rImage.Height());
		if (m_EditMaskOnlyMask)
			BitMaskImage(Blend, *EditingMask(), RectTopLeft, m_Style);
		else {
			img::copy(Blend, BufferCopy->Image->im, RectTopLeft);
			BlendWithBitMask(Blend, *EditingMask(), RectTopLeft, m_Style);
		}
		img::StretchImage(pDC->m_hDC, 
			rClient.left, rClient.top, rClient.Width(), rClient.Height(),
			0, 0, Blend.width(), Blend.height(), 
			Blend, SRCCOPY);
	}
	else {
		img::StretchImage(pDC->m_hDC, 
			rClient.left, rClient.top, rClient.Width(), rClient.Height(),
			rImage.left,  rImage.top,  rImage.Width(),  rImage.Height(), 
			BufferCopy->Image->im, SRCCOPY);
	}
}

PaintingKeySPtr CXView::GetPaintingKey(Size Frame)
{
	assert (Frame < m_Comp.GetDuration());
	PaintingKeySPtr p(new PaintingKey);
	DisplayDetails Details;
	p->m_Style = EffectiveDisplayMode(Frame, &Details);

	if (p->m_Style.Type != ObjectOnly &&
		p->m_Style.Type != BackgroundOnly) 
	{
		if (Details.CombineAllMasks)
			m_Comp.GetSegments().ListMasksWithCombMode(Details.ObjectFrame, p->m_Style.SmoothMask, p->m_MasksWithCombMode);
		else if (Details.pSegment) {
			p->m_MasksWithCombMode.reserve(1);
			p->m_MasksWithCombMode.push_back(Details.pSegment->GetMaskWithCombMode(Details.ObjectFrame));
			if (p->m_MasksWithCombMode[0].combmode.mode == MaskMode::None)
				p->m_MasksWithCombMode[0].combmode.mode = MaskMode::Add; 
		}
	}
	p->m_pObjectFootage = m_Comp.GetFootageSPtr();
	p->m_ObjectFrame = Details.ObjectFrame;
	if (p->m_Style.Type == Composition || p->m_Style.Type == BackgroundOnly) {
		p->m_pBackgroundFootage = m_Comp.GetBackground().GetFootageSPtr();
		p->m_BackgroundFrame = Details.BackgroundFrame;
	}
	return p;
}

void CXView::InvalidateBuffer()
{
	if (m_Comp.IsEmpty())
		return;

	if (IsEditMaskStyleChanged())
		InvalidateBench();

	Base::InvalidateBuffer();
}

void CXView::InvalidateMask()
{
	assert(EditingMask());
	InvalidateBench();
}

static DisplayDetails trash;

DisplayStyle CXView::EffectiveDisplayMode(Size Frame, DisplayDetails * d)
{
	if (!d)
		d = &trash;

	DisplayStyle m = m_Style;
	d->ObjectFrame = Frame;

	if (EditingMask()) {
		m.Type = ObjectOnly;
		return m;
	}

	d->BackgroundFrame = m_Comp.GetBackground().MyFrame(d->ObjectFrame);
	if (d->BackgroundFrame == -1) {
		if (m.Type == BackgroundOnly)
			m.Type = ObjectOnly;
		else if (m.Type == Composition)
			m.Type = MattedObject;
	}

	if (GetPlayMode() == My::PlayFollow && m_pFollowSegment) { // && m_Comp.IsSegmentPtrValid(m_FollowSegment)
		d->CombineAllMasks = false;
		d->pSegment = m_pFollowSegment;
	}
	else {
		d->pSegment = m_Comp.GetSegments().GetActiveSegmentPtr();
		if (d->pSegment) {
			d->CombineAllMasks = false;
			//following lines prevented showing bit-mask for a segment which require smooth-mask calculation, but the calculation was not done yet
			//if (!d->pSegment->GetDataSpan().Inside(int(d->ObjectFrame))) //not GetActiveDataSpan
			//	d->pSegment = 0;
		}
		else {
			d->CombineAllMasks = true;
			d->pSegment = m_Comp.GetSegments().GetTopmostNotNoneSegmentPtr(d->ObjectFrame);
		}
	}

	if (!d->pSegment || !d->pSegment->HasBitMask(d->ObjectFrame)) {
		//no mask
		if (m.Type == Composition || m.Type == MattedObject)
			m.Type = ObjectOnly;
		else if (m.Type == MaskOnly)
			d->pSegment = 0;
	}
	else {
		if (m.SmoothMask && !d->pSegment->HasGreyMask(d->ObjectFrame)) {
			assert(d->pSegment->HasBitMask(d->ObjectFrame));
			m.SmoothMask = false;
		}
	}

	return m;
}

DisplayStyle CXView::CurrentEffectiveDisplayMode(DisplayDetails * d)
{
	return EffectiveDisplayMode(m_Comp.GetPreviewFrame(), d);
}

bool CXView::IsEditMaskStyleChanged()
{
	if (!EditingMask())
		return false;

	if (m_EditMaskOnlyMask == (m_Style.Type == MaskOnly) &&
		m_EditMaskInverse == m_Style.InverseMask &&
		m_EditMaskMatte == m_Style.MatteColor)
		return false;

	m_EditMaskOnlyMask = m_Style.Type == MaskOnly;
	m_EditMaskInverse = m_Style.InverseMask;
	m_EditMaskMatte = m_Style.MatteColor;
	return true;
}

My::TimeControls * CXView::GetTimeControls()
{
	return &theApp.GetFrame().m_TimeControls;
}

void CXView::ProcessEvent(My::Event & e)
{
	if (e.Is<My::Segment::TimeSpanChange>() ||
		e.Is<My::Segment::DataSpanChange>() ||
		e.Is<CXSegment::ParamsChange>() || //the most significant is turning off 'smooth mask' (and undoing this action) since it doesn't change data span but require redraw
		e.Is<CXSegment::MaskModeChange>() ||
		e.Is<My::SegmentSet::Event>())
	{
		InvalidateBuffer();
		return;
	}
	if (e.Is<My::Doc::SelectionChange>())
	{
		InvalidateBuffer();
		return;
	}
	if (e.Is<My::MaskEditor::StateChange>()) //!but not on MaskEditor::MaskChange
	{
		InvalidateBuffer();
		InvalidateBench();	//It's required since buffer may not change (i.e. in ObjectOnly style)
							//but mask should appear or disappear
		return;
	}
	if (e.Is<My::Layer::Event>()) // from Background
	{
		InvalidateBuffer();
		InvalidateBench();
		return;
	}
	My::SequenceView::ProcessEvent(e);
}

void CXView::SetDisplayStyle(const DisplayStyle & m)
{
	if (m != m_Style || m_Style.MatteColor != m.MatteColor) {
		if (m_Style.MatteColor != m.MatteColor)
			InvalidateBorder();
		m_Style = m;
		const My::PropertyMap & map = GetProperties();
		for (My::PropertyMap::const_iterator i = map.begin(); i != map.end(); ++i)
			UpdateProperty(i->second);
		InvalidateBuffer();
	}
}

void CXView::SetRAMPreview(bool on)
{
	if (on != m_RAMPreview) {
		m_RAMPreview = on;
	}
}

void CXView::SetDisplayType(DisplayType t)
{
	DisplayStyle s = m_Style;
	s.Type = t;
	SetDisplayStyle(s);
}

const My::PropertyMap & CXView::GetProperties() const
{
	return * DisplayModeProperty.GetSubProperties(My::EditableList()); //Fedya: "I don't really know what argument to pass to GetSubProperties(...)"
}

My::Property<DisplayStyle>	CXView::DisplayModeProperty;

void CXView::OnRamPreview()
{
	SetRAMPreview(!GetRAMPreview());
}

void CXView::OnUpdateRamPreview(CCmdUI * pCmdUI)
{
	pCmdUI->Enable(TRUE);
	pCmdUI->SetCheck(GetRAMPreview() ? 1 : 0);
}

void CXView::OnFrameProcessed(CXSegment * pSeg, Size Frame) 
{ 
	m_pFollowSegment = pSeg; 
	if (GetPlayMode() == My::PlayFollow) {
		if (idle()) //save CPU resources if it is still busy with prev frame
			m_Comp.SetPreviewFrame(Frame);
	}
}

void CXView::OnSegmentProcessed()
{
	if (GetPlayMode() == My::PlayFollow && !m_Comp.GetSegments().AreThereSegmentsToProcess()) {
		m_pFollowSegment = 0; //enable viewing of any active segment
		SetPlayMode(My::PlayStop);
	}
}

void CXView::OnActivateFollow()
{
	if (m_pFollowSegment != 0 && m_pFollowSegment != m_Comp.GetSegments().GetActiveSegmentPtr())
		InvalidateBuffer();
	//theApp.OnDisplayPreview();
}

void CXView::OnDeactivateFollow()
{
	if (m_pFollowSegment != 0 && m_pFollowSegment != m_Comp.GetSegments().GetActiveSegmentPtr())
		InvalidateBuffer();
}

BOOL CXView::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo)
{
	if (m_MaskEditor.OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
		return TRUE;
	return My::SequenceView::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
}
